package com.sig.util;

import com.sig.util.log.GeneralLog;
import com.sig.util.log.TransactionalAppender;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 事务日志工具
 *
 * @author sig
 * @since 2019/1/25
 */
public final class LogUtil {
    private static ThreadLocal<List<TransactionalAppender>> appendsThreadLocal = new ThreadLocal<>();
    private static ThreadLocal<String> identityThreadLocal = new ThreadLocal<>();
    private static Log errorLog;

    private LogUtil() {
    }

    public static String getLogTransactionalIdentity() {
        String id = identityThreadLocal.get();
        if (id == null) {
            id = System.currentTimeMillis() + "-" + ThreadLocalRandom.current().nextInt(1000);
            identityThreadLocal.set(id);
        }
        return id;
    }

    public static void log(Class<?> clazz, String message) {
        final Log log = LogFactory.getLog(clazz);
        log.info(message);
    }

    public static void log(String name, GeneralLog generalLog) {
        final Log log = LogFactory.getLog(name);
        log.info(generalLog);
    }

    public static void log(String name, String message) {
        final Log log = LogFactory.getLog(name);
        log.info(message);
    }

    public static void errorLog(Throwable t) {
        errorLog(t.getMessage(), t);
    }

    public static void errorLog(String message, Throwable t) {
        errorLog().error(message, t);
    }

    public static void errorLog(Object message, Throwable t) {
        errorLog().error(message, t);
    }

    private static Log errorLog() {
        if (errorLog == null)
            errorLog = LogFactory.getLog("error.log");
        return errorLog;
    }

    /**
     * 提交当前线程事务日志，对当前线程的日志进行写入
     */
    public static void commit() {
        final List<TransactionalAppender> list = getAppends();
        if (CollectionUtils.isEmpty(list))
            return;
        for (TransactionalAppender appender : list)
            appender.commit();
        clear();
    }

    /**
     * 回滚当前线程事务日志
     */
    public static void rollback() {
        final String identity = identityThreadLocal.get();
        if (identity != null)
            identityThreadLocal.set(null);
        final List<TransactionalAppender> list = getAppends();
        if (CollectionUtils.isEmpty(list))
            return;
        for (TransactionalAppender appender : list)
            appender.rollback();
        clear();
    }

    /**
     * 清空当前线程事务日志，以避免内存溢出
     */
    public static void clear() {
        final String identity = identityThreadLocal.get();
        if (identity != null)
            identityThreadLocal.set(null);
        final List<TransactionalAppender> list = getAppends();
        if (CollectionUtils.isEmpty(list))
            return;
        for (TransactionalAppender appender : list)
            appender.clear();
        list.clear();
    }

    /**
     * 当前线程增加appender
     *
     * @param appender
     */
    public static void addAppender(TransactionalAppender appender) {
        List<TransactionalAppender> list = getAppends();
        if (list == null) {
            list = new ArrayList<>();
            appendsThreadLocal.set(list);
        }
        list.add(appender);
    }

    /**
     * 当前线程是否包含appender
     *
     * @param appender
     * @return
     */
    public static boolean containsAppender(TransactionalAppender appender) {
        final List<TransactionalAppender> list = getAppends();
        if (CollectionUtils.isEmpty(list))
            return false;
        return list.contains(appender);
    }

    /**
     * 当前线程移除appender
     *
     * @param appender
     */
    public static void removeAppender(TransactionalAppender appender) {
        final List<TransactionalAppender> list = getAppends();
        if (list != null)
            list.remove(appender);
    }

    private static List<TransactionalAppender> getAppends() {
        return appendsThreadLocal.get();
    }
}
